﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;

// ReSharper disable InconsistentlySynchronizedField

namespace Hsenl.Network {
    public sealed class TcpServer : Acceptor {
        private TcpAcceptor _acceptor;
        private readonly ConcurrentDictionary<long, TcpChannel> _channels = new();
        private readonly NetworkPool<TcpChannel> _channelPool = new();
        private readonly NetworkPool<TcpReceiver> _receiverPool = new();
        private readonly NetworkPool<TcpSender> _senderPool = new();
        private byte[] keepAliveTime;

        public Configure Config { get; private set; }
        public Container Container { get; private set; }

        public event Action<long> OnConnectedEvent;
        public event Action<long, int> OnDisconnectedEvent;

        private readonly object _locker = new();
        private readonly object _createChannelLocker = new();

        public TcpServer(Configure config) {
            this.Config = config ?? throw new ArgumentNullException(nameof(config));

            this.Container = new Container();
            this.Container.Let<TcpChannel>().AllowAutoInjection();
            this.Container.Let<TcpPacketReceiver>().As<IPacketReceiver>().ShareInjection();
            this.Container.Let<TcpPacketSender>().As<IPacketSender>().ShareInjection();
            this.Container.Let<TcpPacketReceiver>().As<IMessageReader>().ShareInjection();
            this.Container.Let<TcpPacketSender>().As<IMessageWriter>().ShareInjection();
        }

        public TcpServer(Configure config, Container container) {
            this.Config = config ?? throw new ArgumentNullException(nameof(config));
            this.Container = container ?? throw new ArgumentNullException(nameof(container));
        }

        public override void StartAccept() {
            this.CheckDisposedException();

            if (this._acceptor != null)
                throw new Exception("Service is already start");

            var localEndPoint = this.Config.GetListenIPEndPoint();
            if (localEndPoint == null)
                throw new Exception($"Listen IPEndPoint is null!");

            this._acceptor = new TcpAcceptor(localEndPoint);
            this._acceptor.Socket.Listen(this.Config.Backlog);
            this._acceptor.OnAcceptedEvent += this.ProcessAccept;
            this._acceptor.StartAccept();

            Log.Info($"Tcp服务器启动成功...{localEndPoint}");
        }

        private void ProcessAccept(Socket acceptSocket) {
            // 进来了一个新的客户端, 给他分配一个专门的通道
            var channel = this._channelPool.TryRent(out var result) ? result : this.CreateChannel();

            if (channel == null)
                throw new Exception("Create Channel Fail!");

            if (this.keepAliveTime == null) {
                this.keepAliveTime = BitConverter.GetBytes(1).Concat(BitConverter.GetBytes(30000)).Concat(BitConverter.GetBytes(5000)).ToArray();
            }

#if NET45_OR_GREATER
            acceptSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            acceptSocket.IOControl(IOControlCode.KeepAliveValues, this.keepAliveTime, null);
#else
            // 跨平台框架：区分 Windows 和非 Windows 配置
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
                // Windows 用 IOControl 配置参数
                acceptSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                acceptSocket.IOControl(IOControlCode.KeepAliveValues, this.keepAliveTime, null);
            }
            else {
                // Linux/macOS 用 Tcp 特定选项配置参数（示例）
                const int TcpKeepAliveTime = 3;
                const int TcpKeepAliveInterval = 17;
                acceptSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                acceptSocket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)TcpKeepAliveTime, 30000); // 空闲 30 秒开始探测
                acceptSocket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)TcpKeepAliveInterval, 5000); // 探针间隔 5 秒
            }
#endif

            acceptSocket.ReceiveBufferSize = this.Config.RecvBufferSize;
            acceptSocket.SendBufferSize = this.Config.SendBufferSize;
            acceptSocket.NoDelay = this.Config.NoDelay;

            var socket = new TcpSocket();
            channel.Service = this;
            channel.UserToken = socket;
            channel.OnMessageReadedEvent += this.OnMessageReaded;
            channel.OnMessageWritedEvent += this.OnMessageWrited;
            channel.OnErrorEvent += this.OnChannelError;
            var channelId = IdHelper.GenerateSnowflakeId();

            // 不可多个 saea 同时接收 一个socket, 可以多个 saea 同时发送一个socket, 但不推荐, 因为发送的消息的顺序不可保证, 导致接受端拆包出错
            TcpReceiver receiver;
            if (!this._receiverPool.TryRentIOCP(out receiver, acceptSocket)) {
                receiver = new TcpReceiver(acceptSocket);
            }

            socket.AddReceiver(receiver);
            receiver.TcpRecvBufferSizePer = this.Config.RecvBufferSizePer;
            receiver.OnClosedEvent += () => { this._receiverPool.Return(receiver); };
            receiver.RecvBufferGetter += channel.GetRecvBuffer;
            receiver.OnRecvDataEvent += data => { this.OnChannelRecvData(channel.ChannelId, data); };
            receiver.OnErrorEvent += channel.OnError;

            TcpSender sender;
            if (!this._senderPool.TryRentIOCP(out sender, acceptSocket)) {
                sender = new TcpSender(acceptSocket);
            }

            socket.AddSender(sender);
            sender.TcpSendBufferSizePer = this.Config.SendBufferSizePer;
            sender.OnClosedEvent += () => { this._senderPool.Return(sender); };
            sender.SendBufferGetter += channel.GetSendBuffer;
            sender.OnSendDataEvent += data => { this.OnChannelSendData(channel.ChannelId, data); };
            sender.OnErrorEvent += channel.OnError;

            channel.Init(channelId);
            this._channels.TryAdd(channel.ChannelId, channel);
            receiver.ReceiveAsync();

            try {
                this.OnConnectedEvent?.Invoke(channel.ChannelId);
            }
            catch (Exception ex) {
                Log.Error(ex);
            }

            foreach (var plug in this.ForeachPlugs<IOnChannelStarted>()) {
                try {
                    plug.Handle(channel.ChannelId);
                }
                catch (Exception ex) {
                    Log.Error(ex);
                }
            }

            Log.Info($"接受客户端'{acceptSocket.RemoteEndPoint}'的连接! 现在有{this._channels.Count}个客户端在连接服务器");
        }

        private TcpChannel CreateChannel() {
            lock (this._createChannelLocker) {
                this.Container.StartStage();
                var channel = this.Container.Resolve<TcpChannel>();
                this.Container.EndStage();
                return channel;
            }
        }

        public override Channel GetChannel(long channelId) {
            this._channels.TryGetValue(channelId, out var channel);
            return channel;
        }

        public override ICollection<long> GetChannelIds() {
            return this._channels.Keys;
        }

        protected override void OnChannelRecvData(long channelId, Memory<byte> data) {
            try {
                base.OnChannelRecvData(channelId, data);
            }
            finally {
                if (this._channels.TryGetValue(channelId, out var channel)) {
                    channel.Read(data);
                }
            }
        }

        protected override void OnChannelError(long channelId, int errorCode) {
            try {
                base.OnChannelError(channelId, errorCode);
            }
            finally {
                if (ErrorCode.fatalSocketErrors.Contains(errorCode) || errorCode > ErrorCode.SocketErrorWatershed) {
                    this.DisconnectChannel(channelId, errorCode);
                }
            }
        }

        public override void Write(long channelId, byte[] data, int offset, int count) {
            var channel = this.GetChannel(channelId);
            if (channel == null) {
                Log.Error("Channel is not exist!");
                return;
            }

            channel.Write(data, offset, count);
        }

        public override void Write(long channelId, Span<byte> data) {
            var channel = this.GetChannel(channelId);
            if (channel == null) {
                Log.Error("Channel is not exist!");
                return;
            }

            channel.Write(data);
        }

        public override void StartSend(long channelId) {
            var channel = this.GetChannel(channelId);
            if (channel == null) {
                Log.Error("Channel is not exist!");
                return;
            }

            ((TcpSocket)channel.UserToken).SendAsync();
        }

        public override void OnUpdate() {
            this._acceptor.StartAccept();
            return;
            foreach (var kv in this._channels) {
                var channel = kv.Value;
                try {
                    channel.OnUpdate();
                }
                catch (Exception e) {
                    Log.Error(e);
                }
            }
        }

        public override void DisconnectChannel(long channelId, int error) {
            lock (this._locker) {
                if (this._channels.TryRemove(channelId, out var channel)) {
                    this.OnChannelDisconnected(channel, error);
                }
            }
        }

        private void OnChannelDisconnected(TcpChannel channel, int error) {
            Log.Info(
                $"一个客户端从服务器断开 '{((TcpSocket)channel.UserToken).Receiver.Socket.RemoteEndPoint}'! 现在有{this._channels?.Count ?? 0}个客户端在连接服务器");
            ((TcpSocket)channel.UserToken).Close();
            channel.Dispose();
            this._channelPool.Return(channel);

            foreach (var plug in this.ForeachPlugs<IOnChannelDisconnected>()) {
                try {
                    plug.Handle(channel.ChannelId, error);
                }
                catch (Exception e) {
                    Log.Error(e);
                }
            }

            try {
                this.OnDisconnectedEvent?.Invoke(channel.ChannelId, error);
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        public void DisconnectAllChannels() {
            lock (this._locker) {
                try {
                    foreach (var kv in this._channels) {
                        var channel = kv.Value;
                        this.OnChannelDisconnected(channel, ErrorCode.ConnectionAborted);
                    }
                }
                finally {
                    this._channels.Clear();
                }
            }
        }

        public override void Dispose() {
            if (this.IsDisposed)
                return;

            base.Dispose();

            this.DisconnectAllChannels();

            this.Config.Dispose();
            this.Config = null;
            this.Container.Dispose();
            this.Container = null;

            this._acceptor.Dispose();
            this._acceptor = null;
            this._channels.Clear();
            this._channelPool.Clear();
            this._receiverPool.Clear();
            this._senderPool.Clear();
            this.OnConnectedEvent = null;
            this.OnDisconnectedEvent = null;
        }

        public class Configure {
            public string LocalIPHost { get; set; }
            public int Port { get; set; }
            public int Backlog { get; set; }
            public bool NoDelay { get; set; }
            public int RecvBufferSize { get; set; }
            public int SendBufferSize { get; set; }
            public int RecvBufferSizePer { get; set; }
            public int SendBufferSizePer { get; set; }

            public IPEndPoint GetListenIPEndPoint() {
                if (IPAddress.TryParse(this.LocalIPHost, out var address)) {
                    var endPoint = new IPEndPoint(address, this.Port);
                    return endPoint;
                }

                return null;
            }

            public void Reset() {
                this.LocalIPHost = null;
                this.Port = 0;
                this.Backlog = 0;
                this.NoDelay = false;
                this.RecvBufferSize = 0;
                this.SendBufferSize = 0;
                this.RecvBufferSizePer = 0;
                this.SendBufferSizePer = 0;
            }

            public void Dispose() {
                this.Reset();
            }
        }
    }
}